#include "tick.h"
#include "params.hpp"
#include <cstdint>
#include <errno.h>
#include <fcntl.h> /* For O_* constants */
#include <memory.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <unistd.h>

static int8_t run = 0;
extern price_t g_hop;

static void
signal_cb(int signal)
{
  run = 0;
}

int
print_usage(const char* exename)
{
  printf("Usage %s <memory key> <tick bin file> <dumped bin file>\n", exename);
  exit(0);
}

int
main(int argc, char** argv)
{
  if (argc != 4)
    print_usage(argv[0]);

  // Open Data File
  FILE* fp1 = fopen(argv[2], "rb");
  if (!fp1) {
    printf("Can not open %s\n", argv[2]);
    return -1;
  }

  FILE* fp2 = fopen(argv[3], "rb");
  if (!fp2) {
    fclose(fp1);
    printf("Can not open %s\n", argv[3]);
    return -2;
  }

  // Get Size
  fseek(fp1, 0, SEEK_END);
  const int64_t size1 = ftell(fp1);
  fseek(fp1, 0, SEEK_SET);

  fseek(fp2, 0, SEEK_END);
  int64_t size2 = ftell(fp2);
  fseek(fp2, 0, SEEK_SET);

  printf("Bolck size = %u\n", Trade::SIZE_OF_ONE_BLOCK);

  int64_t vo_size = size2 / Trade::SIZE_OF_ONE_BLOCK;
  size2 = vo_size * sizeof(Trade::Params);

  const size_t size = size1 + size2 + sizeof(size1) + sizeof(size2);
  printf("file1 %ld, file2 %ld, all size is %lu\n", size1, size2, size);

  // Create Shared Memory
  int fd_mem = shm_open(argv[1], O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
  if (fd_mem == -1) {
    fclose(fp2);
    fclose(fp1);
    printf("Can not create memory %s\n", argv[1]);
    return -3;
  }

  if (ftruncate(fd_mem, size)) {
    shm_unlink(argv[1]);
    fclose(fp2);
    fclose(fp1);
    printf("Set memory size %lu error %d\n", size, errno);
    return -4;
  }

  /* Map the object into the caller's address space. */

#ifdef _LINUX_
  char* data = (char*)mmap64(
    (void*)(NULL), size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_mem, 0);
#endif
#ifdef _MACOS_
  char* data = (char*)mmap(
    (void*)(NULL), size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_mem, 0);
#endif

  if (data == MAP_FAILED) {
    shm_unlink(argv[1]);
    fclose(fp2);
    fclose(fp1);
    printf("Mapping file to memory error\ncd78");
    return -5;
  }

  char* ptr = data;
  *(int64_t*)(ptr) = size1;
  ptr += sizeof(int64_t);
  *(int64_t*)(ptr) = size2;
  ptr += sizeof(int64_t);

  TICK_DATA td = (TICK_DATA)(ptr);

  if (fread(ptr, 1, size1, fp1) != (size_t)(size1)) {
    munmap(data, size);
    shm_unlink(argv[1]);
    fclose(fp2);
    fclose(fp1);
    printf("Read %s error %d\n", argv[2], errno);
    return -6;
  }

  for (int64_t i = 0; i < size1 / (int64_t)(sizeof(Tick)); ++i) {
    td[i].price /= g_hop;
  }
  fclose(fp1);

  ptr += size1;
  Trade::Params* vo = (Trade::Params*)(ptr);
  for (int64_t i = 0; i < vo_size; ++i, ++vo) {
    if (vo->read_data_(fp2)) {
      fclose(fp2);
      munmap(data, size);
      shm_unlink(argv[1]);
      printf("Read %s error %d\n", argv[3], errno);
      return -7;
    }
  }
  fclose(fp2);

  run = 1;

  signal(SIGKILL, signal_cb);
  signal(SIGINT, signal_cb);
  signal(SIGSTOP, signal_cb);
  signal(SIGTERM, signal_cb);

  for (; run == 1;) {
    sleep(1);
  }

  munmap(data, size);
  shm_unlink(argv[1]);

  return (0);
}
